/*
 * Copyright (c) 2009. Silenus Consultoria, S.L.
 */
package es.silenus.detecta.model;

import org.springframework.security.GrantedAuthority;

import org.springframework.security.userdetails.UserDetails;

import java.sql.Timestamp;

import java.util.Locale;


/**
 * An application user. Persistent class, read-write.
 *
 * @author <a href="mailto:malonso@silenus-consultoria.es">Mariano Alonso</a>
 *
 * @since 22-mar-2009 11:04:04
 */
public class User implements java.io.Serializable, UserPrincipal, UserDetails {
	/**
	 * The identifier.
	 */
	private int id;

	/**
	 * The login.
	 */
	private String login;

	/**
	 * The password.
	 */
	private String password;

	/**
	 * The user fullname.
	 */
	private String fullname;

	/**
	 * The email.
	 */
	private String email;

	/**
	 * The language.
	 */
	private String language;

	/**
	 * The date the user was disabled.
	 */
	private Timestamp disableDate;

	/**
	 * The organization.
	 */
	private Organization organization;

	/**
	 * The role.
	 */
	private Role role;

	/**
	 * The role authority.
	 */
	private final RoleGrantedAuthority roleAuthority;

	/**
	 * The authorities.
	 */
	private final GrantedAuthority[] authorities;

/**
   * Constructor.
   */
	public User() {
		role = Role.ROLE_ORGANIZATION_USER;
		roleAuthority = new RoleGrantedAuthority(role);
		authorities = new GrantedAuthority[1];
		authorities[0] = roleAuthority;
	}

	/**
	 * Retrieves the identifier.
	 *
	 * @return the identifier.
	 */
	public int getId() {
		return this.id;
	}

	/**
	 * Sets the identifier.
	 *
	 * @param id the identifier.
	 */
	public void setId(int id) {
		this.id = id;
	}

	/**
	 * Retrieves the login.
	 *
	 * @return the login.
	 */
	public String getLogin() {
		return this.login;
	}

	/**
	 * Sets the login.
	 *
	 * @param login the login.
	 */
	public void setLogin(final String login) {
		this.login = login;
	}

	/**
	 * Retrieves the password.
	 *
	 * @return the password.
	 */
	public String getPassword() {
		return this.password;
	}

	/**
	 * Sets the password.
	 *
	 * @param password the password.
	 */
	public void setPassword(final String password) {
		this.password = password;
	}

	/**
	 * Retrieves the full name.
	 *
	 * @return the full name.
	 */
	public String getFullname() {
		return fullname;
	}

	/**
	 * Sets the full name.
	 *
	 * @param fullname the full name.
	 */
	public void setFullname(final String fullname) {
		this.fullname = fullname;
	}

	/**
	 * Retrieves the email.
	 *
	 * @return the email.
	 */
	public String getEmail() {
		return this.email;
	}

	/**
	 * Sets the email.
	 *
	 * @param email the email.
	 */
	public void setEmail(final String email) {
		this.email = email;
	}

	/**
	 * Retrieves the language.
	 *
	 * @return the language.
	 */
	public String getLanguage() {
		return this.language;
	}

	/**
	 * Sets the language.
	 *
	 * @param language the language.
	 */
	public void setLanguage(final String language) {
		this.language = language;
	}

	/**
	 * Retrieves the disable date.
	 *
	 * @return the disable date.
	 */
	public Timestamp getDisableDate() {
		return disableDate;
	}

	/**
	 * Sets the disable date.
	 *
	 * @param disableDate the disable date.
	 */
	public void setDisableDate(final Timestamp disableDate) {
		this.disableDate = disableDate;
	}

	/**
	 * Retrieves the organization.
	 *
	 * @return the organization.
	 */
	public Organization getOrganization() {
		return this.organization;
	}

	/**
	 * Sets the organization.
	 *
	 * @param organization the organization.
	 */
	public void setOrganization(final Organization organization) {
		this.organization = organization;
	}

	/**
	 * Retrieves the role.
	 *
	 * @return the role.
	 */
	public Role getRole() {
		return this.role;
	}

	/**
	 * Sets the role.
	 *
	 * @param role the role.
	 */
	public void setRole(final Role role) {
		this.role = role;
		this.roleAuthority.setRole(role);
	}

	/**
	 * Returns the name of this principal.
	 *
	 * @return the name of this principal.
	 */
	public String getName() {
		return getLogin();
	}

	/**
	 * Checks if a user is in the given role.
	 *
	 * @param role the role.
	 *
	 * @return true if user is in role, false otherwise.
	 */
	public boolean isUserInRole(final String role) {
		String roleName = (getRole() != null) ? getRole().getKey() : null;

		return role.equals(roleName) || "*".equals(role);
	}

	/**
	 * Checks if user has a given role.
	 *
	 * @param role the role to check.
	 *
	 * @return true if has the role, false otherwise.
	 */
	public boolean isUserInRole(final Role role) {
		return this.getRole().equals(role);
	}

	/**
	 * Checks if the user is a global administrator.
	 *
	 * @return true if user has that role, false otherwise.
	 */
	public boolean isGlobalAdministrator() {
		return isUserInRole(Role.ROLE_ADMINISTRATOR);
	}

	/**
	 * Checks if the user is an organization administrator.
	 *
	 * @return true if user has that role, false otherwise.
	 */
	public boolean isOrganizationAdministrator() {
		return isUserInRole(Role.ROLE_ORGANIZATION_ADMINISTRATOR);
	}

	/**
	 * Checks if the user is an organization designer.
	 *
	 * @return true if user has that role, false otherwise.
	 */
	public boolean isOrganizationDesigner() {
		return isUserInRole(Role.ROLE_ORGANIZATION_DESIGNER);
	}

	/**
	 * Checks if the user is an organization user.
	 *
	 * @return true if user has that role, false otherwise.
	 */
	public boolean isOrganizationUser() {
		return isUserInRole(Role.ROLE_ORGANIZATION_USER);
	}

	/**
	 * Checks if the user is an admin.
	 *
	 * @return true if user has that role, false otherwise.
	 */
	public boolean isAdmin() {
		return isGlobalAdministrator() || isOrganizationAdministrator();
	}

	/**
	 * Retrieves the locale.
	 *
	 * @return the locale.
	 */
	public Locale getLocale() {
		if(getLanguage() != null) {
			String[] parts = getLanguage().split("_");

			switch(parts.length) {
				case 1:
					return new Locale(parts[0]);

				case 2:
					return new Locale(parts[0], parts[1]);

				case 3:
					return new Locale(parts[0], parts[1], parts[2]);
			}
		}

		return Locale.getDefault();
	}

	/**
	 * Sets the locale.
	 *
	 * @param locale the locale.
	 */
	public void setLocale(final Locale locale) {
		String newLanguage = locale.toString();

		setLanguage(newLanguage);
	}

	/**
	 * Checks if the user is disabled or not.
	 *
	 * @return true if the user is disabled, false otherwise.
	 */
	public boolean isDisabled() {
		return getDisableDate() != null;
	}

	/**
	 * Enables or disables the user.
	 *
	 * @param enable the enable flag.
	 */
	public void enable(boolean enable) {
		if(enable) {
			setDisableDate(null);
		} else {
			setDisableDate(new Timestamp(System.currentTimeMillis()));
		}
	}

	/**
	 * Returns the authorities granted to the user. Cannot return <code>null</code>.
	 *
	 * @return the authorities, sorted by natural key (never <code>null</code>)
	 */
	public GrantedAuthority[] getAuthorities() {
		return authorities;
	}

	/**
	 * Returns the username used to authenticate the user. Cannot return <code>null</code>.
	 *
	 * @return the username (never <code>null</code>)
	 */
	public String getUsername() {
		return getLogin();
	}

	/**
	 * Indicates whether the user's account has expired. An expired account cannot be authenticated.
	 *
	 * @return <code>true</code> if the user's account is valid (ie non-expired), <code>false</code> if no longer valid
	 * 				 (ie expired)
	 */
	public boolean isAccountNonExpired() {
		return isEnabled();
	}

	/**
	 * Indicates whether the user is locked or unlocked. A locked user cannot be authenticated.
	 *
	 * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
	 */
	public boolean isAccountNonLocked() {
		return isEnabled();
	}

	/**
	 * Indicates whether the user's credentials (password) has expired. Expired credentials prevent authentication.
	 *
	 * @return <code>true</code> if the user's credentials are valid (ie non-expired), <code>false</code> if no longer
	 * 				 valid (ie expired)
	 */
	public boolean isCredentialsNonExpired() {
		return isEnabled();
	}

	/**
	 * Indicates whether the user is enabled or disabled. A disabled user cannot be authenticated.
	 *
	 * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
	 */
	public boolean isEnabled() {
		return !isDisabled();
	}

	/**
	 * Checks if objects are equal.
	 *
	 * @param o other object.
	 *
	 * @return true if equal, false otherwise.
	 */
	@Override
	public boolean equals(Object o) {
		if(this == o) {
			return true;
		}

		if(!(o instanceof User)) {
			return false;
		}

		User user = (User)o;

		if(getId() != user.getId()) {
			return false;
		}

		return true;
	}

	/**
	 * Calculates the hash of this instance.
	 *
	 * @return the hash code.
	 */
	@Override
	public int hashCode() {
		return getId();
	}

	/**
	 * Stringifies this instance.
	 *
	 * @return the string representation.
	 */
	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder();

		sb.append("User");
		sb.append("{id=").append(getId());
		sb.append(", login='").append(getLogin()).append('\'');
		sb.append('}');

		return sb.toString();
	}
}
